unit AudioOut;

interface

uses
  DebugTools,
  EZStream,
  SimpleThread, PacketList, WaveOut,
  SysUtils, Classes;

type
  TAudioOut = class (TComponent)
  private
    FWaveOut : TWaveOut;
    procedure on_Played(Sender:TObject; AData:pointer; ASize:integer);
  private
    FEZStreamAudio : IEZStreamAudio;
    FListWaveOut : TPacketList;
  private
    FThreadWaveOut : TSimpleThread;
    procedure on_WaveOut_Repeat(Sender:TObject);
  private
    FOnPlayed: TNotifyEvent;
    function GetIsBusy: boolean;
  public
    constructor Create(AOwner:TComponent; AEZStreamAudio:IEZStreamAudio); reintroduce;
    destructor Destroy; override;

    procedure Init;
    procedure DataIn(AData:pointer; ASize:integer);
  published
    property IsBusy : boolean read GetIsBusy;
    property OnPlayed : TNotifyEvent read FOnPlayed write FOnPlayed;
  end;

implementation

{ TAudioOut }

constructor TAudioOut.Create(AOwner: TComponent; AEZStreamAudio: IEZStreamAudio);
begin
  inherited Create(AOwner);

  FEZStreamAudio := AEZStreamAudio;

  FWaveOut := TWaveOut.Create(Self);
  FWaveOut.OnVoicePlayed := on_Played;

  FListWaveOut := TPacketList.Create;

  FThreadWaveOut := TSimpleThread.Create(on_WaveOut_Repeat);
end;

procedure TAudioOut.DataIn(AData: pointer; ASize: integer);
var
  DataOut : pointer;
  SizeOut : integer;
begin
  if FEZStreamAudio.DecodeAudio(AData, ASize, DataOut, SizeOut) then begin
    FListWaveOut.Add(DataOut, SizeOut);
    FThreadWaveOut.WakeUp;
  end;
end;

destructor TAudioOut.Destroy;
begin
  FThreadWaveOut.Terminate;

  // TODO:
//  FreeAndNil(FWaveOut);
//  FreeAndNil(FPacketList);
//  FreeAndNil(FListWaveOut);

  inherited;
end;

function TAudioOut.GetIsBusy: boolean;
const
  DELAY_LIMIT = 4;
begin
  Result := (FWaveOut.DataInBuffer + FListWaveOut.Count) > DELAY_LIMIT;
end;

procedure TAudioOut.Init;
begin
  FListWaveOut.Clear;

  FWaveOut.Stop;

  FWaveOut.Channels :=   FEZStreamAudio.GetChannels;
  FWaveOut.SampleRate := FEZStreamAudio.GetSampleRate;

  FWaveOut.Start;
end;

procedure TAudioOut.on_Played(Sender: TObject; AData: pointer; ASize: integer);
begin
  if Assigned(FOnPlayed) then FOnPlayed(Self);
end;

procedure TAudioOut.on_WaveOut_Repeat(Sender: TObject);
var
  Data : pointer;
  Size : integer;
begin
  while not FThreadWaveOut.Terminated do begin
    while FListWaveOut.Get(Data, Size) do begin
      try
        FWaveOut.Play(Data, Size);
      finally
        if Data <> nil then FreeMem(Data);
      end;
    end;

    FThreadWaveOut.SleepTight;
  end;
end;

end.
